﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX.DirectInput;
using System.Windows.Forms;
using System.Globalization;

namespace MercuryFE
{
    public enum InputDeviceType
    {
        keyboard, mouse, joystick, notsupported
    };

    public enum DeviceTypeAction
    {
        Up, Down, Left, Right, Select, Back, Pause
    };


    public class InputDevice : IDisposable
    {
        InputDeviceType _deviceType = InputDeviceType.notsupported; // set to keyboard by default
        CooperativeLevel _cooperativeLevel = CooperativeLevel.Exclusive | CooperativeLevel.Foreground | CooperativeLevel.NoWinKey;
        //Control _formControl = null;
        Guid _identifier;
        string _productName;
        Device _device;
        IntPtr _handle = IntPtr.Zero;
        int _mouseSensitivity = 10;

        Dictionary<string, DeviceTypeAction> _keyMappings = new Dictionary<string, DeviceTypeAction> 
        {     
              { "UpArrow", DeviceTypeAction.Up }
            , { "DownArrow", DeviceTypeAction.Down }
            , { "LeftArrow", DeviceTypeAction.Left  }
            , { "RightArrow", DeviceTypeAction.Right } 
            , { "D1", DeviceTypeAction.Select }
            , { "D2",DeviceTypeAction.Back }
            , { "P", DeviceTypeAction.Pause }
        };

        Dictionary<int, DeviceTypeAction> _mouseButtonMappings = new Dictionary<int, DeviceTypeAction>
        {
              {0, DeviceTypeAction.Select }
            , {1, DeviceTypeAction.Back }
            , {2, DeviceTypeAction.Pause }
        };



        //int _offset;

        public InputDevice(IntPtr winHandle, InputDeviceType deviceType, Guid identifier, string productName)
        {
            _deviceType = deviceType;
            _identifier = identifier;
            _productName = productName;
            _handle = winHandle;
        }


        public void CreateDevice(DirectInput dinput)
        {
            switch (_deviceType)
            {
                case InputDeviceType.keyboard :
                    KeyboardCreateDevice(dinput);
                    break;
                case InputDeviceType.joystick :
                    JoystickCreateDevice(dinput);
                    break;
                case InputDeviceType.mouse:
                    MouseCreateDevice(dinput);
                    break;

            }


        }

        public InputDeviceState ReadData()
        {
            InputDeviceState state = new InputDeviceState();
            switch (_deviceType)
            {
                case InputDeviceType.keyboard:
                    state = KeyboardReadData();
                    break;
                case InputDeviceType.joystick:
                    state = JoystickReadData();
                    break;
                case InputDeviceType.mouse:
                    state = MouseReadData();
                    break;

            }
            return state;
        }


        public void UnacquireDevice()
        {
            if (_device != null)
            {
                _device.Unacquire();
                //_device.Dispose();
                //_device = null;
            }

        }

        //public void ReleaseDevice()
        //{
        //    if (_device != null)
        //    {
        //        _device.Unacquire();
        //        //_device.Dispose();
        //        //_device = null;
        //    }

        //}


        public void Dispose()
        {
            UnacquireDevice();
            _device = null;
        }

        #region keyboard

        private void KeyboardCreateDevice(DirectInput dinput)
        {

            // create the device
            try
            {
                _device = new Keyboard(dinput);
                ((Keyboard)_device).SetCooperativeLevel(_handle, _cooperativeLevel);
        
            }
            catch (DirectInputException e)
            {
                //MessageBox.Show(e.Message);
                //return;
            }

             ((Keyboard)_device).Properties.BufferSize = 8;


            // acquire the device
            ((Keyboard)_device).Acquire();

        }

        private InputDeviceState KeyboardReadData()
        {
            if (((Keyboard)_device).Acquire().IsFailure)
                return null;

            if (((Keyboard)_device).Poll().IsFailure)
                return null;

           // KeyboardState state = ((Keyboard)_device).GetCurrentState();
            KeyboardState packet = ((Keyboard)_device).GetCurrentState();
            if (SlimDX.Result.Last.IsFailure)
                return null;

            // fix this up to handle key mappings, and return action accordingly

            InputDeviceState ids = new InputDeviceState();

            StringBuilder data = new StringBuilder();
            foreach (Key key in packet.PressedKeys)
            {
                if(_keyMappings.ContainsKey(key.ToString())) {
                    string action = _keyMappings[key.ToString()].ToString();
                    ids.SetValue(action);
                }

                
                data.Append(Enum.GetName(typeof(Key), key) + " ");
            }



            
            ids.Value = data.ToString();
            return ids;

        }

        #endregion


        #region joystick
        private void JoystickCreateDevice(DirectInput dinput)
        {

            // create the device
            try
            {
                _device = new Joystick(dinput, _identifier);
                ((Joystick)_device).SetCooperativeLevel(_handle, CooperativeLevel.Exclusive | CooperativeLevel.Foreground);
            }
            catch (DirectInputException)
            {
                return;
            }

            if (_device == null)
            {
               // MessageBox.Show("There are no joysticks attached to the system.");
                return;
            }
            ((Joystick)_device).Properties.BufferSize = 8;

            // acquire the device
            ((Joystick)_device).Acquire();


        }

        private InputDeviceState JoystickReadData()
        {
            if (((Joystick)_device).Acquire().IsFailure)
                return null;

            if (((Joystick)_device).Poll().IsFailure)
                return null;

            JoystickState state = ((Joystick)_device).GetCurrentState();
            if (SlimDX.Result.Last.IsFailure)
                return null;

            // temp for testing
            string strText = null;
            StringBuilder sb = new StringBuilder();

            sb.Append(" x=" + state.X.ToString(CultureInfo.CurrentCulture));
            sb.Append(" y=" + state.Y.ToString(CultureInfo.CurrentCulture));
            sb.Append(" z=" + state.Z.ToString(CultureInfo.CurrentCulture));

            sb.Append(" rotx=" + state.RotationX.ToString(CultureInfo.CurrentCulture));
            sb.Append(" roty=" + state.RotationY.ToString(CultureInfo.CurrentCulture));
            sb.Append(" rotz=" + state.RotationZ.ToString(CultureInfo.CurrentCulture));

            int[] slider = state.GetSliders();

            sb.Append(" sli1=" + slider[0].ToString(CultureInfo.CurrentCulture));
            sb.Append(" sli2=" + slider[1].ToString(CultureInfo.CurrentCulture));

            int[] pov = state.GetPointOfViewControllers();

            sb.Append(" pov0=" + pov[0].ToString(CultureInfo.CurrentCulture));
            sb.Append(" pov1=" + pov[1].ToString(CultureInfo.CurrentCulture));
            sb.Append(" pov2=" + pov[2].ToString(CultureInfo.CurrentCulture));
            sb.Append(" pov3=" + pov[3].ToString(CultureInfo.CurrentCulture));

            bool[] buttons = state.GetButtons();

            for (int b = 0; b < buttons.Length; b++)
            {
                if (buttons[b])
                    strText += b.ToString("00 ", CultureInfo.CurrentCulture);
            }
            sb.Append(" buttons=" + strText);

            //return sb.ToString();
            InputDeviceState ids = new InputDeviceState();
            return ids;
        }

        #endregion


        #region mouse
        private void MouseCreateDevice(DirectInput dinput)
        {

            // create the device
            try
            {
                _device = new Mouse(dinput);
                ((Mouse)_device).SetCooperativeLevel(_handle, _cooperativeLevel);
            }
            catch (DirectInputException e)
            {
                //MessageBox.Show(e.Message);
                //return;
            }
            ((Mouse)_device).Properties.BufferSize = 8;

            // acquire the device
            ((Mouse)_device).Acquire();

        }

        private InputDeviceState MouseReadData()
        {
            InputDeviceState ids = new InputDeviceState();

            if (((Mouse)_device).Acquire().IsFailure)
                return null;

            if (((Mouse)_device).Poll().IsFailure)
                return null;


            IList<MouseState> bufferedData = ((Mouse)_device).GetBufferedData();
            if (SlimDX.Result.Last.IsFailure || bufferedData.Count == 0)
                return null;

            // get the 'culmination' of all the movements...
            MouseState result = new MouseState();
            foreach (MouseState packet in bufferedData)
            {
                result.X += packet.X;
                result.Y += packet.Y;
                result.Z += packet.Z;
            }

            ids.Up = ((result.Y <  -_mouseSensitivity) || result.Z > 0 ? true : false);
            ids.Down = ((result.Y >  _mouseSensitivity) || result.Z < 0 ? true : false);

            ids.Left = (result.X < -_mouseSensitivity ? true : false);
            ids.Right = (result.X >  _mouseSensitivity ? true : false);

            // foreach button, check if it has been pressed at all... then set true and break
            for (int i = 0; i < 8; i++)
            {
                foreach (MouseState packet in bufferedData)
                {
                    if (packet.IsPressed(i))
                    {
                        ids.Buttons[i] = true;

                        if (_mouseButtonMappings.ContainsKey(i))
                        {
                            string action = _mouseButtonMappings[i].ToString();
                            ids.SetValue(action);

                        }

                        break;
                    }
                }
            }


            return ids;
        }

        #endregion
    }
}
